// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <cstdint>
#include <fstream>
#include <memory>
#include <string>
#include <utility>

#include <boost/asio/io_context.hpp>
#include <glog/logging.h>
#include <nlohmann/json.hpp>

#include "coin2/base/request/http2/session_pool.h"
#include "coin2/base/request/session.h"
#include "coin2/base/request/session_pool.h"
#include "coin2/base/request/i_session_pool.h"
#include "coin2/exchange/base/executor/live_executor.h"
#include "coin2/exchange/base/market/market_exchange_api.h"
#include "coin2/exchange/order/connection.pb.h"

namespace coin2::exchange::base::api_base {

class HttpDriverManager {
  using MarketExchangeApi = coin2::exchange::base::market::MarketExchangeApi;
  using OrderConnectionConfig = coin2::exchange::order::OrderConnectionConfig;
  using ISessionPool = coin2::base::request::ISessionPool;

 public:
  static bool UseHttp2(const MarketExchangeApi& mea, const OrderConnectionConfig& cfg) {
    // prefer to order connection configure
    if (cfg.has_use_http2()) {
      return cfg.use_http2();
    }
    // secondly order gateway information configure
    std::ifstream ifs("data/coin2/order/og_info.json");
    nlohmann::json jf = nlohmann::json::parse(ifs);
    if (jf.find(mea.String()) != jf.end()) {
      auto data = jf[mea.String()];
      if (data.find("use_http2") != data.end()) {
        return data["use_http2"];
      }
    }
    return false;
  }
  static auto GetHttpProxy(const OrderConnectionConfig& cfg) -> std::optional<std::string> {
    if (!cfg.has_http_proxy()) {
      return {};
    }
    return cfg.http_proxy();
  }

  template <class Mea, class Cfg, class... Arg>
  static std::shared_ptr<ISessionPool> Get(Mea&& mea, Cfg&& cfg, Arg&&... args) {
    if (UseHttp2(std::forward<Mea>(mea), std::forward<Cfg>(cfg))) {
      LOG(INFO) << "http/2 implemented";
      using SessionPool = coin2::base::request::http2::SessionPool;
      auto http_driver =
          std::shared_ptr<ISessionPool>(new SessionPool(std::forward<Arg>(args)...));
      if (auto http_proxy = GetHttpProxy(cfg)) {
        http_driver->SetHttpProxy(*http_proxy);
      }
      return http_driver;
    } else {
      LOG(INFO) << "http/1.x implemented";
      using SessionPool = coin2::base::request::SessionPool;
      auto http_driver =
          std::shared_ptr<ISessionPool>(new SessionPool(std::forward<Arg>(args)...));
      if (auto http_proxy = GetHttpProxy(cfg)) {
        http_driver->SetHttpProxy(*http_proxy);
      }
      return http_driver;
    }
  }
};

}  // namespace coin2::exchange::base::api_base
